﻿// $ PURE $ // class name and mean

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Drawing;

using DataSources.Holder;

namespace CodeTable
{
    partial class GridData
    {
        private Hashtable hashtable = null;
        private GridData.Block[,] grid = null;
        private Visual visual = null;

        public event BlockEventHandler BlockChanged;
        public event EventHandler GridChanged;

        public GridData(ScintillaNET.MarginCollection margins)
        {
            this.visual = new Visual(this, margins);
        }

        public void Initialize(IDataHolder dataSource)
        {
            if (dataSource == null)
            {
                this.grid = null;
                this.hashtable = null;
                this.visual.Initialize();
                this.visual.RefreshColumnWidths();
                this.visual.RefreshRowHeights();
                OnGridChanged(new GridEventArgs(null));
            }

            int columnsCount = dataSource.GetSize().Width;
            int rowsCount = dataSource.GetSize().Height;
            this.grid = new GridData.Block[columnsCount, rowsCount];
            this.hashtable = new Hashtable();
            FillGridFromStringHashtable(dataSource.GetEntries(), this.visual.margins);
            InternalSetEventsToBlocks();

            this.visual.Initialize();
            this.visual.RefreshColumnWidths();
            this.visual.RefreshRowHeights();

            OnGridChanged(new GridEventArgs(dataSource.GetEntries()));
        }

        private void FillGridFromStringHashtable(Hashtable stringHashtable, ScintillaNET.MarginCollection margins)
        {
            if (hashtable == null || grid == null || stringHashtable == null)
                return;
            foreach (DictionaryEntry entry in stringHashtable)
            {
                string hash = entry.Key as string;
                Point gridCoordinate = DataSources.Utils.Hash2GridCoordinate(hash);
                Block newBlock = InternalAddBlock(hash, gridCoordinate, margins, false);
                newBlock.Text = entry.Value as string;
            }
        }

        public int MaxColumns
        {
            get
            {
                return grid == null ? 0 : grid.GetLength(0);
            }
        }

        public int MaxRows
        {
            get
            {
                return grid == null ? 0 : grid.GetLength(1);
            }
        }
        
        public GridData.Block GetBlock(Point gridCoordinate) // TODO: use special struct/class for gridCoordinates instead of Point
        {
            if (gridCoordinate.X < 0 || gridCoordinate.X >= MaxColumns)
                return null;
            if (gridCoordinate.Y < 0 || gridCoordinate.Y >= MaxRows)
                return null;
            return grid[gridCoordinate.X, gridCoordinate.Y];
        }

        public GridData.Block AddBlock(Point gridCoordinate)
        {
            string hash = "R" + gridCoordinate.Y.ToString() + "C" + gridCoordinate.X.ToString();
            return InternalAddBlock(hash, gridCoordinate, this.visual.margins);
        }

        private GridData.Block InternalAddBlock(string hash, Point gridCoordinate, ScintillaNET.MarginCollection margins, bool setEvent = true)
        {
            System.Diagnostics.Trace.Assert(GetBlock(gridCoordinate) == null);

            GridData.Block codeBlock = new GridData.Block(gridCoordinate, margins);
            if (setEvent)
            {
                codeBlock.TextChanged += new EventHandler(DataBlock_TextChanged);
            }
            hashtable.Add(hash, codeBlock);
            grid[gridCoordinate.X, gridCoordinate.Y] = codeBlock;
            return codeBlock;
        }

        private void InternalSetEventsToBlocks()
        {
            foreach (DictionaryEntry entry in hashtable)
            {
                Block block = entry.Value as Block;
                if (block != null)
                {
                    block.TextChanged += new EventHandler(DataBlock_TextChanged);
                }
            } 
        }

        public Hashtable Blocks
        {
            get
            {
                return hashtable;
            }
        }

        public Visual VisualAspect
        {
            get
            {
                return visual;
            }
        }

        public void InputBlock_TextChanged(object sender, EventArgs e)
        {
            BlockInput inputBlock = sender as BlockInput;
            if (inputBlock == null || !inputBlock.Visible)
                return;
            Block block = GetBlock(inputBlock.CurrentGridCoordinate);
            if (block == null || block.Text == inputBlock.Text)
                return;

            block.Text = inputBlock.Text;
        }

        public void DataBlock_TextChanged(object sender, EventArgs e)
        {
            Block block = sender as Block; 
            if (block == null)
                return;

            OnBlockChanged(new BlockEventArgs(block.GridCoordinate, block.Text, block.TextSize));
        }

        protected virtual void OnBlockChanged(BlockEventArgs e)
        {
            // check on append row needed
            if (e.codeText != string.Empty && e.gridCoordinate.Y == grid.GetLength(1) - 1)
            {
                InternalAppendRow();
            }
            // update internal sizing, initiate update for view controls locations.
            e.needsRefresh =
                visual.RefreshColumnWidth(e.gridCoordinate) ||
                visual.RefreshRowHeight(e.gridCoordinate);

            // call event handlers
            BlockEventHandler handler = this.BlockChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnGridChanged(EventArgs e)
        {
            // call event handlers
            EventHandler handler = this.GridChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        private void InternalAppendRow()
        {
            Utils.AppendRowToArray<GridData.Block>(ref grid);
        }

        public Hashtable GetBlocks()
        {
            Hashtable stringHashtable = new Hashtable();
            foreach (DictionaryEntry entry in hashtable)
            {
                Block block = entry.Value as Block;
                stringHashtable.Add(entry.Key, block.Text);
            }
            return stringHashtable;
        }
    }
}
